/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package Services.ArchivoService;

import Services.AppService;
import Services.UsuarioService.UsuarioService;
import dataBase.HibernateUtil;
import exeptions.GeneralException;
import exeptions.MsgException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import mapeos.Archivo;
import mapeos.Grupo;
import mapeos.Tipoarchivo;
import mapeos.Usuario;
import mapeos.Usuariogrupo;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;
import org.hibernate.exception.ConstraintViolationException;
import utils.ArchUtils;

/**
 *
 * @author ivanpianetti
 */
public class ArchivoService implements IArchivoService {

    private static IArchivoService unArchivo;
    private static String nombreCarpetaHome = "PandoraBox";

    public static IArchivoService getInstance() {
        if (unArchivo == null || !(unArchivo instanceof IArchivoService)) {
            unArchivo = new ArchivoService();
        }
        return unArchivo;
    }

    /**
     * Obtiene todos los archivos pertenecientes a un Usuario.
     * Icluye las carpetas que creó él y las que otros usuarios le compartieron
     * @param pUsuario
     * @return
     * @throws GeneralException 
     */
    public List<Archivo> getAllByUsuario(Usuario pUsuario) throws GeneralException {
        List<Archivo> archivos = new ArrayList<Archivo>();
        List<Grupo> grupos = AppService.getInstance().getGrupoService().getAllByUsuario(pUsuario);
        Session s = HibernateUtil.currentSession();
        try {
            for (Grupo grupo : grupos) {
                archivos.addAll(this.getAllByGrupo(grupo));
            }
            archivos.addAll(this.getCarpetasCompartidas(pUsuario));
        } catch (HibernateException he) {
            throw new HibernateException(he);
        } finally {
            if (s.isOpen()) {
                s.close();
            }
        }
        HashMap<Integer, Archivo> archivosMap = new HashMap<Integer, Archivo>();
        for (Archivo arch : archivos) {
            archivosMap.put(arch.getIdArchivo(), arch);
        }
        archivos = new ArrayList<Archivo>(archivosMap.values());
        return archivos;
    }

    /**
     * Obtiene los Archivos que tienen como padre al 
     * Archivo de tipo Carpeta que se le pasa por parámetro.
     * Si la carpeta que se le pasa por parámetro es la carpeta Home 
     * del Usuario, se le agrega al lista de archivos las carpetas "adoptadas"
     * @param pCarpeta Carpeta
     * @return Archivos contendios en Carpeta
     * @throws GeneralException 
     */
    public List<Archivo> getAllByCarpeta(Archivo pCarpeta) throws GeneralException {
        List<Archivo> archivos = new ArrayList<Archivo>();
        if (pCarpeta.isCarpeta()) {
            Session s = HibernateUtil.currentSession();
            try {
                Transaction t = s.beginTransaction();
                archivos.addAll((List<Archivo>) s.createCriteria(Archivo.class).
                        add(Restrictions.eq("carpetaPadre.idArchivo", pCarpeta.getIdArchivo())).
                        add(Restrictions.not(Restrictions.eq("idArchivo", pCarpeta.getIdArchivo()))).
                        addOrder(Order.asc("idArchivo")).
                        list());
                if (pCarpeta.isCarpetaHome()) { //Si es la carpeta HOME agrega las carpetas que son compatidas y que no son de ella
                    archivos.addAll(this.getCarpetasCompartidas(UsuarioService.getUsuarioActual()));
                }
            } catch (HibernateException he) {
                throw new HibernateException(he);
            } finally {
                if (s.isOpen()) {
                    s.close();
                }
            }
        }
        HashMap<Integer, Archivo> archivosMap = new HashMap<Integer, Archivo>();
        for (Archivo arch : archivos) {
            archivosMap.put(arch.getIdArchivo(), arch);
        }
        archivos = new ArrayList<Archivo>(archivosMap.values());
        return archivos;
    }

    public List<Archivo> getAllByGrupo(Grupo pGrupo) throws GeneralException {
        List<Archivo> archivos = new ArrayList<Archivo>();
        Session s = HibernateUtil.currentSession();
        try {
            Transaction t = s.beginTransaction();
            archivos = (List<Archivo>) s.createCriteria(Archivo.class).
                    add(Restrictions.eq("grupo.idGrupo", pGrupo.getIdGrupo())).
                    addOrder(Order.asc("idArchivo")).
                    list();
        } catch (HibernateException he) {
            throw new HibernateException(he);
        } finally {
            if (s.isOpen()) {
                s.close();
            }
        }
        return archivos;
    }

    /**
     * Obtiene las carpetas que otros usuarios le compartieron.
     * Estas carpetas no son hijas de la carpeta HOME del usuario, 
     * sino que son carpetas "adoptadas".
     * @param pUsuario
     * @return
     * @throws GeneralException 
     */
    public List<Archivo> getCarpetasCompartidas(Usuario pUsuario) throws GeneralException {
        List<Archivo> archivos = new ArrayList<Archivo>();
        List<Grupo> listaUsuariosGrupos = AppService.getInstance().getGrupoService().getAllByUsuario(pUsuario);
        Session s = HibernateUtil.currentSession();
        try {
            for (Grupo grupo : listaUsuariosGrupos) {
                archivos.addAll(this.getAllByGrupo(grupo));
            }
        } catch (HibernateException he) {
            throw new HibernateException(he);
        } finally {
            if (s.isOpen()) {
                s.close();
            }
        }
        List<Archivo> archivosResp = new ArrayList<Archivo>();
        for (Archivo arch : archivos) {
            if (arch.isCarpetaCompartible()) { // verifica que la carpeta es del tipo compartible
                archivosResp.add(arch);
            }
        }
        return archivosResp;
    }

    /**
     * Obtiene la carpeta Home de PandoraBox de un Usuario
     * @param pUsuario
     * @return
     * @throws GeneralException 
     */
    public Archivo getCarpetaHomeByUsuario(Usuario pUsuario) throws GeneralException {
        List<Archivo> archivos = this.getAllByUsuario(pUsuario);
        for (Archivo arch : archivos) {
            if (arch.isCarpetaHome()) {
                return arch;
            }
        }
        return null;
    }

    public Archivo getById(int pId) throws GeneralException {
        Archivo archivo = new Archivo();
        Session s = HibernateUtil.currentSession();
        try {
            Transaction t = s.beginTransaction();
            archivo = (Archivo) s.createCriteria(Archivo.class).
                    add(Restrictions.eq("idArchivo", pId)).
                    uniqueResult();
        } catch (HibernateException he) {
            throw new GeneralException(MsgException.getMsgException("General.ErrorAlConsultar", he));
        } finally {
            if (s.isOpen()) {
                s.close();
            }
        }
        return archivo;
    }

    public void insert(Archivo eArchivo) throws GeneralException {
        Session s = HibernateUtil.currentSession();
        try {
            Transaction t = s.beginTransaction();
            s.save(eArchivo);
            t.commit();
        } catch (ConstraintViolationException he) {
            throw new GeneralException(MsgException.getMsgException("Archivo.Existe", he));
        } catch (HibernateException he) {
            throw new GeneralException(MsgException.getMsgException("General.ErrorAlInsertar", he));
        } finally {
            if (s.isOpen()) {
                s.close();
            }
        }
    }

    public void update(Archivo eArchivo) throws GeneralException {
        Session s = HibernateUtil.currentSession();
        try {
            Transaction t = s.beginTransaction();
            s.update(eArchivo);
            t.commit();
        } catch (HibernateException he) {
            throw new HibernateException(he);
        } finally {
            if (s.isOpen()) {
                s.close();
            }
        }
    }

    /**
     * @param eArchivo
     * @throws GeneralException 
     */
    public void delete(Archivo eArchivo) throws GeneralException {
        Session s = HibernateUtil.currentSession();
        try {
            if (eArchivo.isCarpetaCompartible()) {
                this.abandonarCarpetaCompartida(eArchivo);
            } else {
                Transaction t = s.beginTransaction();
                s.delete(eArchivo);
                t.commit();
            }
        } catch (HibernateException he) {
            throw new HibernateException(he);
        } finally {
            if (s.isOpen()) {
                s.close();
            }
        }
    }

    /**
     * Elimina al usuario actual del grupo perteneciente a la carpeta compartida.
     * Si la carpeta comparida no tiene mas usuarios se eliminan también
     * todos los archivos
     * @param eArchivo
     * @throws GeneralException 
     */
    public void abandonarCarpetaCompartida(Archivo eArchivo) throws GeneralException {
        Session s = HibernateUtil.currentSession();
        try {
            Transaction t = s.beginTransaction();
            if (eArchivo.isCarpetaCompartible()) {
                Grupo grupo = eArchivo.getGrupo();
                Usuariogrupo userGrupo = (Usuariogrupo) s.createCriteria(Usuariogrupo.class).
                        add(Restrictions.eq("grupo.idGrupo", grupo.getIdGrupo())).
                        add(Restrictions.eq("usuario.idUsuario", UsuarioService.getUsuarioActual().getIdUsuario())).
                        uniqueResult();
                List<Usuario> usuarios = AppService.getInstance().getUsuarioService().getAllByGrupo(grupo);
                if (usuarios.size() == 1) { // si es el ultimo usuario en el grupo => se eliminan los archivos
                    List<Archivo> archivos = AppService.getInstance().getArchivoService().getAllByGrupo(grupo);
                    for (Archivo archivo : archivos) {
                        s = HibernateUtil.currentSession();
                        t = s.beginTransaction();
                        s.delete(archivo);
                        t.commit();
                    }
                } else {
                    AppService.getInstance().getUsuarioGrupoService().delete(userGrupo);
                }
            }
        } catch (HibernateException he) {
            throw new GeneralException(MsgException.getMsgException("Archivo.ErrorAlAbandonarCarpeta", he));
        } finally {
            if (s.isOpen()) {
                s.close();
            }
        }
    }

    /**
     * Este método crea la carpeta Home cuando un Usuario
     * es creado.
     * @param pUsuario
     * @throws GeneralException 
     */
    public void crearCarpetaHome(Usuario pUsuario) throws GeneralException {
        //GRUPO
        Grupo unGrupo = new Grupo();
        AppService.getInstance().getGrupoService().insert(unGrupo);
        //USUARIO GRUPO
        Usuariogrupo unUsuarioGrupo = new Usuariogrupo(pUsuario, unGrupo);
        AppService.getInstance().getUsuarioGrupoService().insert(unUsuarioGrupo);
        //TIPO ARCHIVO
        Tipoarchivo tipoArchCarpeta = AppService.getInstance().getTipoArchivoService().getByTipo(ArchUtils.tipoCarpeta);
        //ARCHIVO
        Archivo carpeta = new Archivo(tipoArchCarpeta, unGrupo, nombreCarpetaHome, null, null);
        this.insert(carpeta);
        carpeta.setCarpetaPadre(carpeta);//Es hija de ella misma => es la carpeta home PandoraBox
        this.update(carpeta);
    }

    /**
     * Crea carpetas con un nuevo grupo o no dependiendo
     * si son hijas de la carpeta home o nietas.
     * Solo las hijas de la carpeta home pueden ser compartidas.
     * Todas las hijas de esta carpeta pertenecerán al 
     * mismo grupo que su madre, evitando así que se puedan compartir.
     * Está prohibido compartir carpetas hijas siendo que su madre ya está compartida.
     * #ATENCION: El nombre de la nueva carpeta será el mismo que el de su madre,
     * deberá revertir el nombre de su madre al salir de este método
     * @param pUsuario
     * @throws GeneralException 
     */
    public void crearCarpeta(Archivo carpetaActual) throws GeneralException {
        if (carpetaActual.isCarpeta()) {
            Tipoarchivo tipoCarpeta = carpetaActual.getTipoarchivo();
            Usuario userActual = UsuarioService.getUsuarioActual();
            //Si la carpeta actual es la home entonces la nueva carpeta
            //      debe estár en un nuevo GRUPO para que pueda ser compartida.
            //Si la carpeta actual NO es la home entonces la nueva carpeta
            //      tendrá que permanecer al mismo GRUPO que su carpeta madre (carpeta actual).

            Archivo carpeta = new Archivo(tipoCarpeta, null, carpetaActual.getNombreArchivo(), carpetaActual, null);
            if (carpetaActual.isCarpetaHome()) {
                //NUEVO GRUPO
                Grupo nuevoGrupo = new Grupo();
                AppService.getInstance().getGrupoService().insert(nuevoGrupo);
                //NUEVO USUARIO GRUPO
                Usuariogrupo unUsuarioGrupo = new Usuariogrupo(userActual, nuevoGrupo);
                AppService.getInstance().getUsuarioGrupoService().insert(unUsuarioGrupo);
                carpeta.setGrupo(nuevoGrupo);
            } else {
                Grupo grupoActual = carpetaActual.getGrupo();
                carpeta.setGrupo(grupoActual);
            }
            this.insert(carpeta);
        } else {
            throw new GeneralException(MsgException.getMsgException("La nueva carpeta debe estar dentro de otra existente."));
        }

    }

    public void compartirCarpeta(Archivo pCarpeta, Usuario user) throws GeneralException {
        if (pCarpeta.isCarpetaCompartible()) {
            if (user == null || user.getNombreUsuario() == null) {
                throw new GeneralException(MsgException.getMsgException("Usuario.NoExiste"));
            } else {
                Grupo grupo = pCarpeta.getGrupo();
                List<Usuario> usuarios = AppService.getInstance().getUsuarioService().getAllByGrupo(grupo);
                boolean existe = false;
                for (Usuario u : usuarios) {
                    if (u.getIdUsuario() == user.getIdUsuario()) {
                        existe = true;
                    }
                }
                if (!existe) {
                    //Si el usuario no existe en el grupo, se lo agrega al grupo
                    Usuariogrupo unUsuarioGrupo = new Usuariogrupo(user, grupo);
                    AppService.getInstance().getUsuarioGrupoService().insert(unUsuarioGrupo);
                }
            }
        } else {
            throw new GeneralException(MsgException.getMsgException("No se puede compartir este elemento."));
        }
    }

    public void subirArchivo(String filePath, Archivo folder) throws GeneralException {
        if (filePath != null && folder != null) {
            if (folder.isCarpeta()) {
                try {
                    byte[] datosArchivo = ArchUtils.getBytesFromFile(filePath);
                    String tipoArchSTR = ArchUtils.getTipoArchivo(filePath);
                    Tipoarchivo tipoArch = AppService.getInstance().getTipoArchivoService().getByTipo(tipoArchSTR);
                    String nombreCompleto = ArchUtils.getNombreArchivo(filePath) + "." + ArchUtils.getExtencionArchivo(filePath);

                    Archivo nuevoArchivo = new Archivo(tipoArch, folder.getGrupo(), nombreCompleto, folder, datosArchivo);
                    AppService.getInstance().getArchivoService().insert(nuevoArchivo);
                } catch (GeneralException ex) {
                    throw new GeneralException(MsgException.getMsgException("Archivo.ErrorAlGuardar", ex));
                } catch (IOException ex) {
                    throw new GeneralException(MsgException.getMsgException("Archivo.ErrorAlGuardar", ex));
                }
            } else {
                throw new GeneralException(MsgException.getMsgException("Archivo.ErrorAlGuardar"));
            }
        } else {
            throw new GeneralException(MsgException.getMsgException("Archivo.ErrorAlGuardar"));
        }

    }

    public void descargarArchivo(String pathArchivo, Archivo eArchivo) throws GeneralException {
        if (pathArchivo != null & eArchivo != null) {
            if (!eArchivo.isCarpeta()) {
                ArchUtils.guardarArchivo2(pathArchivo, eArchivo);
            } else {
                throw new GeneralException(MsgException.getMsgException("Solo se pueden descargar archivos."));
            }
        } else {
            throw new GeneralException(MsgException.getMsgException("Archivo.ErrorAlDescargar"));
        }
    }
}
